﻿using Asion.Dapper.SqlBuilder;
using Asion.Dapper.SqlBuilder.Condition;
using Dapper;
using System;
using System.Collections.Generic;
using System.ComponentModel.Design.Serialization;
using System.Linq;
using System.Text;


namespace Asion.Dapper
{
    public interface IWhere : ICondition
    {

    }

    public class EqualsWhere : BaseCondition, IWhere, IConditionDynamicParameters, IGetSql
    {
        private IDictionary<string, string> where;

        public EqualsWhere(IDictionary<string, string> dic)
        {
            where = dic;
        }

        public DynamicParameters GetDynamicParameters()
        {
            GetSql();
            return DynamicParam;
        }

        public override string BuildSql()
        {
            var result = string.Empty;
            if (where == null || !where.Any())
            {
                throw new Exception("sql must have where condition");
            }
            StringBuilder sql = new StringBuilder();
            int i = 0;
            foreach (var column in this.where)
            {
                if (i > 0)
                    sql.Append(" AND ");
                var mapping = _custom_table_mappinghandle.GetCustomTableMapping().GetColumnMapping(column.Key);
                if (mapping == null)
                {
                    throw new ArgumentException($" column {column.Key} is not exist");
                }

                var formula = ComplexOperatorFactory.GetComplexCondtionOperatorFormula(CondtionOperator.Equals);
                sql.AppendFormat(formula, _encap_sulation.EncapSulation(mapping.DataColumnName), _encap_sulation.EncapCloumnParam(mapping.DataColumnName));
                AddDynamicParam(mapping.DataColumnName, column.Value, mapping.GetDataColumnType(), CondtionOperator.Equals);
                // not param clean
                i++;
            }
            if (i > 0)
            {
                result = $" WHERE {sql.ToString()}";
            }
            return result;
        }
    }







    public class ComplexWhere : BaseCondition, IWhere, IConditionDynamicParameters, IPrintSql
    {
        internal List<ComplexCondtion> Conditions;

        internal bool IsChild { get; set; }

        public ComplexWhere()
        {
            Conditions = new List<ComplexCondtion>();
        }



        public ComplexWhere(bool isChild) : base()
        {
            IsChild = true;
        }

        public ComplexWhere(List<ComplexCondtion> conditions) : base()
        {
            Conditions = conditions;
        }

        internal void SetChild(bool isChild)
        {
            IsChild = isChild;
        }




        /// <summary>
        /// Greater than
        /// </summary>
        /// <param name="column"></param>
        /// <param name="value"></param>
        /// <param name="isEquals"> equalse</param>
        /// <param name="columnNick"></param>
        /// <returns></returns>
        public ComplexWhere Gt(string column, object value, bool isEquals = false, string columnNick = "")
        {
            var oprator = isEquals ? CondtionOperator.GreaterAndEquals : CondtionOperator.Greater;
            Conditions.Add(new ComplexCondtion(column, oprator, value, columnNick));
            return this;
        }

        /// <summary>
        /// less than
        /// </summary>
        /// <param name="column"></param>
        /// <param name="value"></param>
        /// <param name="isEquals"> equalse</param>
        /// <param name="columnNick"></param>
        /// <returns></returns>
        public ComplexWhere Lt(string column, object value, bool isEquals = false, string columnNick = "")
        {
            var oprator = isEquals ? CondtionOperator.LessAndEquals : CondtionOperator.Less;
            Conditions.Add(new ComplexCondtion(column, oprator, value, columnNick));
            return this;
        }

        /// <summary>
        /// equals
        /// <para>sql: colums=1</para>
        /// </summary>
        /// <param name="column"></param>
        /// <param name="value"></param>
        /// <param name="columnNick"></param>
        /// <returns></returns>
        public ComplexWhere Eq(string column, object value, string columnNick = "")
        {

            Conditions.Add(new ComplexCondtion(column, CondtionOperator.Equals, value, columnNick));
            return this;
        }


        /// <summary>
        /// 
        /// <para>DATE(order_date) = '2021-03-15';</para>
        /// </summary>
        /// <param name="column"></param>
        /// <param name="value"></param>
        /// <param name="columnNick"></param>
        /// <returns></returns>
        public ComplexWhere EqDate(string column, DateTime value, string columnNick = "")
        {
            Conditions.Add(new ComplexCondtion(column, CondtionOperator.EqualsDate, value.ToShortDateString(), columnNick));
            return this;
        }

        public ComplexWhere NotEq(string column, object value, string columnNick = "")
        {

            Conditions.Add(new ComplexCondtion(column, CondtionOperator.NotEquals, value, columnNick));
            return this;
        }

        public ComplexWhere Like(string column, object value, string columnNick = "")
        {

            Conditions.Add(new ComplexCondtion(column, CondtionOperator.Like, value, columnNick));
            return this;
        }

        public ComplexWhere RightLike(string column, object value, string columnNick = "")
        {

            Conditions.Add(new ComplexCondtion(column, CondtionOperator.RightLike, value, columnNick));
            return this;
        }

        public ComplexWhere In(string column, object[] values, string columnNick = "")
        {
            Conditions.Add(new ComplexCondtion(column, CondtionOperator.In, values, columnNick));
            return this;
        }

        public ComplexWhere In(string column, int[] values, string columnNick = "")
        {
            Conditions.Add(new ComplexCondtion(column, CondtionOperator.In, values, columnNick));
            return this;
        }

        public ComplexWhere NotIn(string column, object[] values, string columnNick = "")
        {
            Conditions.Add(new ComplexCondtion(column, CondtionOperator.NotIn, values, columnNick));
            return this;
        }



        private bool GetIsParenthesis()
        {
            int count = Conditions.Count();
            if (IsChild && count != 1)
            {
                return true;
            }
            return false;
        }





        public DynamicParameters GetDynamicParameters()
        {
            return DynamicParam;
        }



        public override string BuildSql()
        {
            var result = string.Empty;
            if (Conditions == null || !Conditions.Any())
            {
                throw new Exception("sql must have where condition");
            }

            bool hasParenthesis = GetIsParenthesis();

            StringBuilder sb = new StringBuilder();
            int i = 0;
            foreach (var column in this.Conditions)
            {

                if (i > 0)
                    sb.Append(" AND ");
                var mapping = _custom_table_mappinghandle.GetCustomTableMapping().GetColumnMapping(column.Column);
                if (mapping == null)
                {
                    throw new ArgumentException($" column {column.Column} is not exist in where dic");
                }

                var formula = ComplexOperatorFactory.GetComplexCondtionOperatorFormula(column.Operator);
                sb.AppendFormat(formula, _encap_sulation.EncapSulation(mapping.DataColumnName), _encap_sulation.EncapCloumnParam(column.Guid));
                AddDynamicParam(column.Guid, column.Value, mapping.GetDataColumnType(), column.Operator);
                i++;


            }
            if (i > 0 && !IsChild)
            {
                result = $" WHERE {sb.ToString()}";
            }
            else
            {
                if (GetIsParenthesis())
                {
                    result = $"({sb.ToString()})";
                }
                else
                {
                    result = sb.ToString();
                }
            }
            return result;
        }
    }



    public class MultComplexWhere : BaseCondition, IWhere, IConditionDynamicParameters, IGetSql
    {


        internal List<MultComplex> conditions;

        public MultComplexWhere()
        {
            conditions = new List<MultComplex>();
        }


        public void And(Func<ComplexWhere> func)
        {
            conditions.Add(new MultComplex(MultComplexOperator.And, func.Invoke()));
        }





        public void Or(Func<ComplexWhere> func)
        {
            conditions.Add(new MultComplex(MultComplexOperator.Or, func.Invoke()));
        }





        public DynamicParameters GetDynamicParameters()
        {
            return DynamicParam;
        }








        public override string BuildSql()
        {
            var result = string.Empty;
            if (conditions == null || !conditions.Any())
            {
                throw new Exception("sql must have where condition");
            }
            StringBuilder sb = new StringBuilder();
            int i = 0;
            foreach (var column in this.conditions)
            {
                if (i != 0)
                {
                    switch (column.Operator)
                    {
                        case MultComplexOperator.And:
                            sb.Append(" AND ");
                            break;
                        case MultComplexOperator.Or:
                            sb.Append(" OR ");
                            break;
                        default:
                            throw new NotImplementedException();
                            break;
                    }
                }
                var conditon = column.Value;
                conditon.SetChild(true);
                var sql = conditon.GetSql();
                AddDynamicParam(conditon.GetDynamicParameters());
                sb.Append(sql);
                i++;
            }
            if (i > 0)
            {
                result = $" WHERE {sb.ToString()}";
            }
            return result;
        }
    }


    internal class MultComplex
    {
        internal MultComplex(MultComplexOperator operators, ComplexWhere value)
        {
            this.Operator = operators;
            this.Value = value;
        }




        internal MultComplexOperator Operator { get; set; }


        internal ComplexWhere Value { get; set; }
    }




}

